package org.pada.ext;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.pada.ext.annotation.Ext;
import org.pada.ext.annotation.Exts;
import org.pada.ext.annotation.IExtPt;
import org.pada.ext.annotation.SysExtPt;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.annotation.PostConstruct;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.*;

/**
 * @author pengshen
 * @date 2023.12.29 16:52
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class ExtHub {

    private static final Map<String, IExt> EXT_MAP = new HashMap<>();

    private final List<IExt> extList;

    @Ext(extPt = "#")
    private static class DefaultExt implements IExt {
    }

    public static IExt getExt(IExtPt extPt) {
        if (EXT_MAP.containsKey(extPt.getIdentity())) {
            return EXT_MAP.get(extPt.getIdentity());
        }
        // 寻找父节点
        final List<String> parent = extPt.getParentIdentities();
        if (Objects.nonNull(parent)) {
            final Optional<String> first = parent.stream().filter(EXT_MAP::containsKey).findFirst();
            if (first.isPresent()) {
                return EXT_MAP.get(first.get());
            }
        }
        throw new RuntimeException("can not find ext pt: " + extPt.getIdentity());
    }

    @PostConstruct
    public void register() {
        extList.forEach(this::register);
    }

    private void register(IExt ext) {
        final Ext extension = ext.getClass().getAnnotation(Ext.class);
        Optional.ofNullable(extension).ifPresent(x -> {
            if (StringUtils.hasText(x.extPt())) {
                put(x.extPt(), ext);
            }
            if (!x.clz().equals(SysExtPt.class)) {
                put(x.clz().getName(), ext);
            }
        });

        try {
            Annotation[] annotations = ext.getClass().getDeclaredAnnotations();
            for (Annotation annotation : annotations) {
                Ext superExt = annotation.annotationType().getAnnotation(Ext.class);
                if (Objects.nonNull(superExt)) {
                    Method value = annotation.annotationType().getDeclaredMethod("value");
                    IExtPt[] invoke = (IExtPt[])value.invoke(annotation);
                    Arrays.stream(invoke).forEach(t -> put(t.getIdentity(), ext));
                    break;
                }
            }
        } catch (Exception e) {
            log.error("parse sub ext annotation exception {}", ext.getClass().getName());
        }

        final Exts extensions = ext.getClass().getAnnotation(Exts.class);
        Optional.ofNullable(extensions).map(Exts::extPts)
                .ifPresent(x -> Arrays.stream(x).filter(Objects::nonNull).forEach(y -> put(y.extPt(), ext)));
    }

    private void put(String extPt, IExt ext) {
        if (EXT_MAP.containsKey(extPt)) {
            log.error("ext pt {} exist, please check!", extPt);
        } else {
            EXT_MAP.put(extPt, ext);
        }
    }

}
